/////////////////////////////////////////////////////////////////////////////// // Run Actions Multiple Times.jsx // V 1.0, last updated 2013-12-14 // This script allows you to run up to two actions multiple times. Very useful // for altering animations. // // For more info and the latest version, go to // http://wrestlingwithtext.com/giffing // // TODO: Documentation, some error handling for edge case situations. // Currently the code can handle having n actions, so changing actionCnt // to something greater than 2 would work just fine. Could easily add another // field to allow users to define the number of actions to run each iteration. // Could use some code clean-up and some best-practices... as soon as I // figure out what those are. /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // MAIN SCRIPT: /////////////////////////////////////////////////////////////////////////////// var dlg; var stringOptions = []; var actionOptnLists = []; var actionCnt = 2; function myRun() { var count = parseInt(dlg.edittext.text); var doc = activeDocument; for( var i = 0; i < count; i++ ) { for ( var k = 0; k < actionCnt; k++ ) { var actionIndex = parseInt(dlg['dropdownlist' + k].selection); var myStringOptions = actionOptnLists[k][actionIndex]; if ( myStringOptions.action != undefined ) { app.doAction(myStringOptions.action.name, myStringOptions.actionSet.name); } } } setIntegerValue('frameCount', count); for ( var k = 0; k < actionCnt; k++ ) { var actionIndex = parseInt(dlg['dropdownlist' + k].selection); setIntegerValue('actionIndex' + k, actionIndex); } dlg.close(0); } function myShow() { var myFontSelection = "none"; dlg = new Window ('dialog', 'Run action(s) multiple times'); var item; var actionSets = GetActionSetInfo(); for ( var k = 0; k < actionCnt; k++ ) { var dropdownlist = dlg['dropdownlist' + k] = dlg.add("dropdownlist"); var stringOptions = []; item = dropdownlist.add ('item', 'Select an action:'); stringOptions.push({}); for ( var i = 0; i < actionSets.length; i++ ) { var actionSet = actionSets[i]; if ( undefined != actionSet.children ) { item = dropdownlist.add('item', '' + actionSet.name); stringOptions.push({actionSet: actionSet}); for ( var j = 0; j < actionSet.children.length; j++ ) { var action = actionSet.children[j]; item = dropdownlist.add('item', '- ' + action.toString()); stringOptions.push({action: action, actionSet: actionSet}); } } } actionOptnLists.push(stringOptions); var actionIndex = getIntegerValue('actionIndex' + k, 0); dlg['dropdownlist' + k].selection = dlg['dropdownlist' + k].items[actionIndex]; } dlg.cntgrp = dlg.add("group"); dlg.cntgrp.add("statictext", undefined, "Times to run:"); dlg.edittext = dlg.cntgrp.add("edittext", undefined, getIntegerValue('frameCount', 2)); dlg.edittext.characters = 3; dlg.btnLyrs = dlg.cntgrp.add('button', undefined, 'Use layer count(' + activeDocument.layers.length + ')'); dlg.btnLyrs.onClick = function() { dlg.edittext.text = activeDocument.layers.length; } dlg.historyCheck = dlg.add("checkbox", undefined, 'Only one history state'); if ( getIntegerValue('oneHistory', 1) ) dlg.historyCheck.value = true; dlg.btnRun = dlg.add('button', undefined , 'Run'); dlg.btnRun.onClick = function() { var actionIndex = parseInt(dlg.dropdownlist0.selection); var myStringOptions = actionOptnLists['0'][actionIndex]; if ( myStringOptions.action == undefined ) { alert('You must select an action.' + ((myStringOptions.actionSet != undefined)?' You have selected an action set.':'')); } else { var oneHistory = dlg.historyCheck.value; setIntegerValue('oneHistory', oneHistory ? 1 : 0 ); if ( oneHistory ) { app.activeDocument.suspendHistory('Run ' + myStringOptions.action.name + ' ' + parseInt(dlg.edittext.text) + ' time' + ((parseInt(dlg.edittext.text) > 1) ? 's' : ''), 'myRun()'); } else { myRun(); } } }; var suspendTextChange = false; dlg.edittext.onChange = function() { if ( suspendTextChange ) return; suspendTextChange = true; var val = parseInt(this.text); if ( isNaN(val) || !val ) val = 1; this.text = val; suspendTextChange = false; } dlg.orientation = 'column'; dlg.center(); dlg.show(); } myShow(); /////////////////////////////////////////////////////////////////////////////// // HELPER FUNCTIONS: /////////////////////////////////////////////////////////////////////////////// function setIntegerValue(name, value) { value = parseInt(value); var desc = new ActionDescriptor();// create a descriptor to hold value desc.putInteger(0, value); app.putCustomOptions(name, desc, true );// store the descriptor by unique name } function getIntegerValue(name, dflt) { if ( dflt == undefined ) dflt = null; try{ var desc = app.getCustomOptions(name); } catch(e) { return dflt; } if ( undefined != desc ) { // has counter stored return desc.getInteger(0); } else {// not stored so set default return dflt; } } /////////////////////////////////////////////////////////////////////////////// // Function: GetActionSetInfo // Usage: walk all the items in the action palette and record the action set // names and all the action children // Input: // Return: the array of all the ActionData // Note: This will throw an error during a normal execution. There is a bug // in Photoshop that makes it impossible to get an acurate count of the number // of action sets. /////////////////////////////////////////////////////////////////////////////// function GetActionSetInfo() { var actionSetInfo = new Array(); var setCounter = 1; while ( true ) { var ref = new ActionReference(); ref.putIndex( charIDToTypeID( 'ASet' ), setCounter ); var desc = undefined; try { desc = executeActionGet( ref ); } catch( e ) { break; } var actionData = new ActionData(); if ( desc.hasKey( charIDToTypeID( 'Nm ' ) ) ) { actionData.name = desc.getString( charIDToTypeID( 'Nm ' ) ); } var numberChildren = 0; if ( desc.hasKey( charIDToTypeID( 'NmbC' ) ) ) { numberChildren = desc.getInteger( charIDToTypeID( 'NmbC' ) ); } if ( numberChildren ) { actionData.children = GetActionInfo( setCounter, numberChildren ); actionSetInfo.push( actionData ); } setCounter++; } return actionSetInfo; } /////////////////////////////////////////////////////////////////////////////// // Function: GetActionInfo // Usage: used when walking through all the actions in the action set // Input: action set index, number of actions in this action set // Return: true or false, true if file or folder is to be displayed /////////////////////////////////////////////////////////////////////////////// function GetActionInfo( setIndex, numChildren ) { var actionInfo = new Array(); for ( var i = 1; i <= numChildren; i++ ) { var ref = new ActionReference(); ref.putIndex( charIDToTypeID( 'Actn' ), i ); ref.putIndex( charIDToTypeID( 'ASet' ), setIndex ); var desc = undefined; desc = executeActionGet( ref ); var actionData = new ActionData(); if ( desc.hasKey( charIDToTypeID( 'Nm ' ) ) ) { actionData.name = desc.getString( charIDToTypeID( 'Nm ' ) ); } var numberChildren = 0; if ( desc.hasKey( charIDToTypeID( 'NmbC' ) ) ) { numberChildren = desc.getInteger( charIDToTypeID( 'NmbC' ) ); } actionInfo.push( actionData ); } return actionInfo; } /////////////////////////////////////////////////////////////////////////////// // Function: ActionData // Usage: this could be an action set or an action // Input: // Return: a new Object of ActionData /////////////////////////////////////////////////////////////////////////////// function ActionData() { this.name = ""; this.children = undefined; this.toString = function () { var strTemp = this.name; if ( undefined != this.children ) { for ( var i = 0; i < this.children.length; i++ ) { strTemp += "\n" + this.children[i].toString(); } } return strTemp; } }